/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "StdAfx.h"
#include "dataregkey.h"
#include "Phantom_InternalErrors.h"

CDataRegKey::CDataRegKey(void)
{
	OpenState = 0;
	Handle = 0;
	//Parent = "";
	Path = "";
  KeyDelim = ":";

  if(!sType){
		sType = 1;
		sType = Factory_RegisterData((CData*)new CDataRegKey());
	}
	Type = sType;
	DataName = "regkey";
}

CDataRegKey::~CDataRegKey(void)
{
	// If it is open, close it...
	Close();
}

SSI_BOOL CDataRegKey::DeleteData(CData* In)
{
	delete ((CDataRegKey*)In);
	return 1;
}

SSI_BOOL CDataRegKey::DataFromString(const String& sData)
{
	return 0;
}

String CDataRegKey::GetString(void)
{
	String Ret;
	//Ret.Format("Handle: %d Open: %d Path: '%s' Parent: '%s'", Handle, OpenState, Path.Str(), Parent.Str());
  Ret.Format("Handle: %d Open: %d Path: '%s'", Handle, OpenState, Path.Str());
	return Ret;
}

SSI_BOOL CDataRegKey::CopyData(CData* Other)
{
	if(Type != Other->Type) return 0;
  if(Other == SSI_NULL) return 0;
  CDataRegKey* Temp = (CDataRegKey*)Other;
	OpenState = Temp->OpenState;
	Handle = Temp->Handle;
	Path = Temp->Path;
	//Parent = Temp->Parent;

  return 1;
}

SSI_INT64 CDataRegKey::SizeOf(void)
{
	//return sizeof(Data);
	return 0;
}

CData* CDataRegKey::MakeData(void)
{
	return (CData*) new CDataRegKey();
}

CData* CDataRegKey::operator_equal(CData* InData, OpInfoT* Info)
{	
	if(InData->GetType() != Type) return SSI_NULL;
	CDataRegKey* Temp = (CDataRegKey*)InData;
	OpenState = -1; // Open state is inconclusive Temp->OpenState;
	Handle = Temp->Handle;
	Path = Temp->Path;
	//Parent = Temp->Parent;

	return (CData*)this;
}


CStatus CDataRegKey::InitData(void)
{
	CFunctionDecl aNewFunc;

	CVariable aVar;
	aVar.Name = "OpenState";
	aVar.SetType(CDataInt::GetRegisteredType());
	AddVariable(aVar);

	aVar.Name = "Handle";
	aVar.SetType(CDataInt::GetRegisteredType());
	AddVariable(aVar);

	aVar.Name = "Path";
	aVar.SetType(CDataString::GetRegisteredType());
	AddVariable(aVar);

	//aVar.Name = "Parent";
	//aVar.SetType(CDataString::GetRegisteredType());
	//AddVariable(aVar);

	aVar.Name = "PathDelim";
	aVar.SetType(CDataString::GetRegisteredType());
	AddVariable(aVar);

  
  aNewFunc.Clear();
	aNewFunc.Name = "Open";
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor(this);
	AddFunction(aNewFunc);
	
	aNewFunc.Clear();
	aNewFunc.Name = "Close";
	aNewFunc.ReturnType = CDataVoid::GetRegisteredType();
	aNewFunc.SetProcessor(this);
	AddFunction(aNewFunc);
  
  aNewFunc.Clear();
	aNewFunc.Name = "GetValues";
	aNewFunc.addType(CDataString::GetRegisteredType());
  aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor(this);
	AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "GetValueCount";
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor(this);
	AddFunction(aNewFunc);


	return STAT_OK;
}

CStatus CDataRegKey::SSI_UpdateVariable(CVariable& aVar, SSI_BOOL bGet)
{
	CDataString aStr;
	CDataInt anInt;

	if(aVar.Name == "OpenState"){
		if(bGet){
			anInt.Data = OpenState;
			return aVar.SetData((CData*)&aStr);
		}else{
			OpenState = ((CDataInt&)aVar.GetWorkingData()).Data;
			return STAT_OK;
		}
	}else if(aVar.Name == "Handle"){
		if(bGet){
			anInt.Data = reinterpret_cast<SSI_INT64>(Handle);
			return aVar.SetData((CData*)&aStr);
		}else{
			Handle = (HKEY)((CDataInt&)aVar.GetWorkingData()).Data;
			return STAT_OK;
		}
	}else	if(aVar.Name == "Path"){
		if(bGet){
			aStr.Data = Path;
			return aVar.SetData((CData*)&aStr);
		}else{
			Path = ((CDataString&)aVar.GetWorkingData()).Data;
			return STAT_OK;
		}
  }else	if(aVar.Name == "PathDelim"){
		if(bGet){
			aStr.Data = KeyDelim;
			return aVar.SetData((CData*)&aStr);
		}else{
      String s = ((CDataString&)aVar.GetWorkingData()).Data;
      if(s.GetLength() > 0){
			  KeyDelim = s;
      }
			return STAT_OK;
		}
	//}else	if(aVar.Name == "Parent"){
	//	if(bGet){
	//		aStr.Data = Parent;
	//		return aVar.SetData((CData*)&aStr);
	//	}else{
	//		Parent = ((CDataString&)aVar.GetWorkingData()).Data;
	//		return STAT_OK;
	//	}
	}
	MAKEERR2(ERR_P00101, aVar.Name.Str(), DataName.Str());
}

void CDataRegKey::SSI_FunctionProcessor(SSI_FunctionAPI& theAPI)
{
	CStatus RetStat;

	if(theAPI.m_FunctionName == "Open"){
		CDataInt Ret;
		Ret.Data = Open();
		theAPI.m_ReturnStatus = theAPI.SetReturnData(&Ret);
		return;
	}
	if(theAPI.m_FunctionName == "Close"){
		Close();
		return;
	}

	if(theAPI.m_FunctionName == "GetValues"){
		CDataString Ret;
		String nDelim = SSI_EXTRACT(CDataString, 0);
    String vDelim = SSI_EXTRACT(CDataString, 1);
		Ret.Data = GetValues(nDelim, vDelim);
		theAPI.m_ReturnStatus = theAPI.SetReturnData(&Ret);
		return;
	}
	if(theAPI.m_FunctionName == "GetValueCount"){
		CDataInt Ret;
		Ret.Data = GetValueCount();
		theAPI.m_ReturnStatus = theAPI.SetReturnData(&Ret);
		return;
	}

}

SSI_INT64 CDataRegKey::GetRegisteredType(void)
{
	if(!sType){
		CDataRegKey aData;
	}
	return sType;
}



SSI_INT64 CDataRegKey::Open(void)
{
	Close();
	HKEY aKey;
  HKEY Base = NULL;

  String Ret;
  String LocPath = Path;
  if(LocPath.Parse(KeyDelim, Ret, 0, KeyDelim.GetLength()) < 0){
    Ret = LocPath;
    LocPath = "";
  }
  if(Ret == "HKEY_CLASSES_ROOT"){
    Base = HKEY_CLASSES_ROOT;
  }else if(Ret == "HKEY_CURRENT_USER"){
    Base = HKEY_CURRENT_USER;
  }else if(Ret == "HKEY_LOCAL_MACHINE"){
    Base = HKEY_LOCAL_MACHINE;
  }else if(Ret == "HKEY_USERS"){
    Base = HKEY_USERS;
  }else if(Ret == "HKEY_DYN_DATA"){
    Base = HKEY_DYN_DATA;
  }

  

  while(LocPath.Parse(KeyDelim, Ret, 0, KeyDelim.GetLength()) >= 0){
    if(!RegOpenKeyEx(Base, Ret.Str(), 0, KEY_READ, &aKey) == ERROR_SUCCESS){
      RegCloseKey(Base);
      return 0;
    }else{
      RegCloseKey(Base);
	    Base = aKey;
    }
    
    int i = 0;
  }

	if(RegOpenKeyEx(Base, LocPath.Str(), 0, KEY_READ, &aKey) == ERROR_SUCCESS){
		Handle = aKey;
    RegCloseKey(Base);
    OpenState = 1;
		return 1;
	}
  RegCloseKey(Base);

	return 0;
}

SSI_INT64 CDataRegKey::Close(){
	OpenState = 0;
  if(Handle != 0){
		int iRet = RegCloseKey(Handle);
    Handle = 0;
    return iRet;
	}
	return 1;
}

String CDataRegKey::GetValues(String& NameDelim, String& ValueDelim)
{
	// Check if Open
	// Check if Handle Valid

  if(Handle == 0){
    return "";
  }

	String Ret;

	CHAR     KeyBuff[MAX_PATH]; 
  BYTE     ValBuff[MAX_PATH];
 
  DWORD i;
	DWORD Count;
  DWORD retCode;
  DWORD Type;
	DWORD BuffSz = MAX_PATH-1;
  DWORD ValSz = MAX_PATH-1;

	Count = GetValueCount();

	for(i = 0; i < Count; i++){
		// This can be used to get sub-keys
		// retCode = RegEnumKeyEx(Handle, i, KeyBuff, &BuffSz, NULL, NULL, NULL, &ftLastWriteTime); 

		//LONG RegEnumValue(HKEY hKey, DWORD dwIndex, LPTSTR lpValueName, LPDWORD lpcValueName, LPDWORD lpReserved, LPDWORD lpType,  LPBYTE lpData,  LPDWORD lpcbData);

		KeyBuff[0] = 0;
    ValBuff[0] = 0;
		retCode = RegEnumValue(Handle, i, KeyBuff, &BuffSz, NULL, &Type, &ValBuff[0], &ValSz);
    ValBuff[ValSz] = 0;

		Ret = Ret + KeyBuff;
    Ret = Ret + ValueDelim;
    Ret = Ret + ValueFromType(Type, ValBuff);
		Ret = Ret + NameDelim;
		BuffSz = MAX_PATH;
    ValSz = MAX_PATH;
  }
  if(Count > 0){
    int iLen = NameDelim.GetLength();
    for(i = 0; i < iLen; i++){
      Ret.chop();
    }
  }
	return Ret;
}

String CDataRegKey::ValueFromType(DWORD Type, BYTE* In){
  String Ret;

  if(In == 0){
    return "";
  }

  DWORD* Val;
  if(Type == REG_BINARY){
    Val = (DWORD*)&In[0];
    Ret.Format("0x%08X", *Val);
  }else if(Type == REG_DWORD || Type == REG_DWORD_LITTLE_ENDIAN || Type == REG_DWORD_BIG_ENDIAN){
    Val = (DWORD*)&In[0];
    Ret.Format("0x%08X", *Val);
  }else if(Type == REG_SZ){
    Ret.Format("%s", (char*)In);
  }else{
    Ret = "{Unsupported}";
  }
  return Ret;

}

SSI_INT64 CDataRegKey::GetValueCount(){
  if(Handle == 0){
    return -1;
  }

  DWORD Count;
  DWORD retCode;
	retCode = RegQueryInfoKey(Handle, 0, 0, 0, 0, 0, 0, &Count, 0, 0, 0, 0);
	return (SSI_INT64)Count;
}

//SSI_INT64 CDataRegKey::GetValue(){
//	DWORD Count;
//  DWORD retCode;
//	retCode = RegQueryInfoKey(Handle, 0, 0, 0, 0, 0, 0, &Count, 0, 0, 0, 0);
//	return (SSI_INT64)Count;
//}

SSI_INT64 CDataRegKey::Register(SSI_INT64 NewType){
	sType = NewType;
	return 1;
}

void CDataRegKey::UnRegister(){
	sType = 0;
}